from pandac.PandaModules import *
from direct.showbase.DirectObject import DirectObject
from pmcore.basecom import BaseComponent
import re

class mousePicker(DirectObject,BaseComponent):
    name = "NodePath mouse selector"
    weight = 1
    _cursel = []
    _oldsel = None
    verbose = True
    dragging = False
    dragenable = False
    oldMPos = Point2()
    sfilter = ""
    
    def __init__(self):
        BaseComponent.__init__(self)
        self.provides = (
            {"name":"get-selected-np",
            "value":self.getSelected,
            "version":"0.1"},
            {"name":"npselector-reset-scene",
            "value":self.reset_scene,
            "version":"1.0"}
        )
        self.event_provides = {"np-selector-select":"send when NodePath selected"}
    def after_direct_import(self):
        if self.settings.has_key("verbose"):
            if self.settings["verbose"].lower() == "true":
                self.verbose = True
        if self.settings.has_key("drag"):
            if self.settings["drag"].lower() == "true":
                self.dragenable = True
        if self.settings.has_key("filter"):
            self.sfilter = self.settings["filter"]
            
        self.picker         = CollisionTraverser()            
        self.pickerQ        = CollisionHandlerQueue()         
        pickerCollN         = CollisionNode('mouseRay')       
        pickerCamN          = base.camera.attachNewNode(pickerCollN) 
        pickerCollN.setFromCollideMask(BitMask32.bit(1))         
        pickerCollN.setIntoCollideMask(BitMask32.allOff())         
        self.pickerRay      = CollisionRay()                
        pickerCollN.addSolid(self.pickerRay)      
        self.picker.addCollider(pickerCamN, self.pickerQ) 
        #base.accept("mouse1",self.pick)
        #base.accept("mouse1-up",self.onEndDrag)
        self.accept("mouse1",self.pick)
        self.accept("mouse1-up",self.onEndDrag)
        he = get_service("event-translator-has-event")
        if he:
            if he("mouse1"):
                add_listener("npms-mouse1","mouse1",self.pick)
            if he("mouse1-up"):
                add_listener("npms-mouse1-up","mouse1-up",self.onEndDrag)
        
        add_listener("npms-loader-bl","model-loader-before-load",self.deselect)
        add_listener("npms-loader-bs","model-loader-before-save",self.deselect)
        add_listener("npms-loader-as","model-loader-after-save",self.select_old)
        
        self.setCMask(render,(pickerCollN,self.pickerRay,base.camera))
        taskMgr.doMethodLater(0.1, self.drag,"np-selector-drag-task")
    
    def onEndDrag(self):
        self.dragging = False
    
    def pick(self,args=[]):
        if base.mouseWatcherNode.hasMouse():
            mpos = base.mouseWatcherNode.getMouse()
            self.pickerRay.setFromLens(base.camNode, mpos.getX(), mpos.getY())
            self.picker.traverse(render)
            if self.pickerQ.getNumEntries()>0:
                self.pickerQ.sortEntries()
                for e in xrange(self.pickerQ.getNumEntries()):
                    entry=self.pickerQ.getEntry(e)
                    if entry.getIntoNodePath().node().isGeomNode():
                        if self.check_filter(entry.getIntoNodePath()):
                            if self._cursel:
                                self._oldsel = self._cursel                            
                            if entry.getIntoNodePath() not in self._cursel:
                                self._cursel = [entry.getIntoNodePath(),]
                                for np in self._cursel:
                                    np.showTightBounds()
                                if self._oldsel:
                                    for np in self._oldsel:
                                        np.hideBounds()
                                send_event("np-selector-select",[self._cursel])
                                messenger.send("np-selector-select",[self._cursel])
                            if self._cursel:
                                self.dragging = True
                                self.oldMPos = Point2(mpos)
                                send_event("np-selector-start-drag",[self.oldMPos])
                                messenger.send("np-selector-start-drag",[self.oldMPos])

                            break

    def check_filter(self,np):
        if self.sfilter == "":
            return True
        else:
            fltrs = self.sfilter.split(";")
            for flt in fltrs:
                f = flt.split(":")
                if len(f) == 2:
                    if f[0] == "tag":
                        if not np.hasTag(f[1]):
                            return False
                        print "TAG:",f[1] 
                    if f[0] == "name":
                        f[1] = f[1].replace(".","\.")
                        f[1] = f[1].replace("?",".")
                        f[1] = f[1].replace("*",".*")
                        f[1] = "^" + f[1] + "$"
                        print "NAME:",f[1], np.getName()
                        if not re.search(f[1],np.getName()):
                            return False
        return True

    
    def drag(self,task):
        if self.dragenable and self.dragging:
            if base.mouseWatcherNode.hasMouse():
                mpos = base.mouseWatcherNode.getMouse()
                for np in self._cursel:
                    nearPoint = Point3()
                    farPoint = Point3()
                    base.camLens.extrude(self.oldMPos, nearPoint, farPoint) 
                    fromvec = Vec3(farPoint - nearPoint)
                    fromvec.normalize()
                    nearPoint = Point3()
                    farPoint = Point3()
                    base.camLens.extrude(mpos, nearPoint, farPoint) 
                    tovec = Vec3(farPoint - nearPoint)
                    tovec.normalize()
                    scale = Vec3(np.getPos(render) - base.cam.getPos(render)).length()
                    v = (tovec - fromvec)#*scale
                    v.normalize()

                    np.setPos(np,np.getRelativePoint(base.cam,tovec*scale) - 
                                np.getRelativePoint(base.cam,fromvec*scale))
                self.oldMPos = Point2(mpos)
                
        return task.cont#task.again
    
    def deselect(self, *args, **kwargs):
        self._oldsel = self._cursel
        self._cursel = []
        if self._cursel:
            for np in self._cursel:
                np.showTightBounds()
        if self._oldsel:
            for np in self._oldsel:
                np.hideBounds()
        send_event("np-selector-select",[self._cursel])
        messenger.send("np-selector-select",[self._cursel])
    
    def select_old(self, *args, **kwargs):
        buf = self._cursel
        self._cursel = self._oldsel
        self._oldsel = buf
        if self._cursel:
            for np in self._cursel:
                np.showTightBounds()
        if self._oldsel:
            for np in self._oldsel:
                np.hideBounds()
        send_event("np-selector-select",[self._cursel])
        messenger.send("np-selector-select",[self._cursel])
        
        
    def setCMask(self,np,excludes = []):
        if len(np.getChildren())>0:
            for child in np.getChildren():
                if child not in excludes:
                    self.setCMask(child,excludes)
        else:
            np.node().setIntoCollideMask(BitMask32.bit(1))
            if self.verbose:
                print np, np.node().isGeomNode()
    
    def setCMask_test(self,np,excludes = []):
        gnodes = np.findAllMatches('**/+GeomNode')
        for child in gnodes:
            child.node().setIntoCollideMask(BitMask32.bit(1))

    
    def reset_scene(self,np):
        self.setCMask(np)
        
    def getSelected(self):
        return self._cursel
